#include "MyComment.h"

#define MAX_EVENT 3000
int count = 0;                 // 用来统计有多少客户端连接，有时候也会作为下标使用
LOGIN_INFO loginInfoArr[1000]; // 登录信息数组
int loginCount = 0;            // 登录的人数，有时候也作为下标用
sqlite3 *pDB = NULL;           // 数据库的指针
int ret = 0;

void saveChatHistory(PVCH *pSc) // 保存聊天记录到文件中
{
    int fd = open("chatHistory.dat", O_WRONLY | O_APPEND);
    // printf("聊天记录保存,内容是：%s %s %s %ld", pSc->selfName, pSc->destName, pSc->text, pSc->t);
    write(fd, pSc, sizeof(PVCH));
    close(fd);
}
int execSql(sqlite3 *db, char *pSql) // 执行SQL语句
{
    char *msg = NULL;
    sqlite3_exec(db, pSql, 0, 0, &msg);
    if (msg != NULL)
    {
        printf("执行SQL语句失败 %s 错误的原因是：%s\n", pSql, msg);
        return -1;
    }
    // printf("执行SQL语句成功\n");
    return 1;
}
int querySql(sqlite3 *db, char *pSql, QUREY_RST *pQR) // 执行查询语句
{
    char *msg = NULL;
    int data = sqlite3_get_table(db, pSql, &pQR->pzResult, &pQR->row, &pQR->col, &msg);
    if (msg != NULL)
    {
        printf("查询数据库失败,SQL语句是:%s 原因是：%s\n", pSql, msg);
        return -1;
    }
    else
    {
        return 1;
    }
}
int work(int sID, MSG msg)
{
    if (msg.type == StudentEnroll) // 学生的注册
    {
        SEN *Stu = (SEN *)msg.buf; // 把缓冲区的类型转为学生注册
        char zcSql[512];
        char zcSql2[512];
        msg.type = no;
        char sql[512];
        ret = 0;
        sprintf(sql, "select * from UserState where name = '%s';", Stu->name);
        QUREY_RST qr;
        // 执行查询语句
        querySql(pDB, sql, &qr);
        // 如果结果的行数是>0的，说明数据库中有记录，那么用户名重复，注册失败
        if (qr.row > 0)
        {
            msg.type = no;
        }
        else
        {
            msg.type = ok;
            // 把用户名和密码插入到数据表中
            sprintf(zcSql, "INSERT INTO student (name, pwd, sex, score) VALUES ( '%s', '%s', '%s', '0')",
                    Stu->name, Stu->pwd, Stu->sex);
            ret = execSql(pDB, zcSql);
            error("插入数据表失败", ret);
            sprintf(zcSql2, "INSERT INTO UserState (name,Teacher,Student,[Group],Private) VALUES ( '%s','0','S','0','0')",
                    Stu->name);
            ret = execSql(pDB, zcSql2);
            error("插入数据表失败", ret);
        }
        ret = send(sID, &msg, sizeof(msg), 0);
        error("发送数据出错", ret);
    }
    else if (msg.type == StudentLogin) // 学生的的登录
    {
        // 把缓冲区的类型转为注册，因为注册和登录用的是同一个结构体
        SEN *Stu = (SEN *)msg.buf;
        // 因为登录成功了，所以把消息类型改为ok，再发回去
        msg.type = no;
        char cxsql[512];
        sprintf(cxsql, "select * from student where name = '%s' AND pwd = '%s';", Stu->name, Stu->pwd);
        QUREY_RST qr;
        // 执行查询语句
        querySql(pDB, cxsql, &qr);
        // 如果结果的行数是>0的，说明数据库中有记录，那么登陆成功
        if (qr.row > 0)
        {
            // 因为登录成功了，所以把消息类型改为ok，再发回去
            msg.type = ok;
            // 把登录成功的用户名和地址保存起来
            strcpy(loginInfoArr[loginCount].name, Stu->name);
            loginInfoArr[loginCount].fd = sID; // 登录成功后记住用户的套接字
            loginCount++;                      // 登录的人数加1
            // 把包含有一条聊天记录的消息发送给客户端
            int ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
        else
        {
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
    }
    else if (msg.type == TeacherEnroll) // 老师注册
    {
        TEN *Tech = (TEN *)msg.buf; // 把缓冲区的类型转为学生注册
        char zcSql[512];
        char zcSql2[512];
        msg.type = no;
        char sql[512];
        ret = 0;
        sprintf(sql, "select * from UserState where name = '%s';", Tech->name);
        QUREY_RST qr;
        // 执行查询语句
        querySql(pDB, sql, &qr);
        // 如果结果的行数是>0的，说明数据库中有记录，那么用户名重复，注册失败
        if (qr.row > 0)
        {
            msg.type = no;
        }
        else
        {
            msg.type = ok;
            // 把用户名和密码插入到数据表中
            sprintf(zcSql, "INSERT INTO teacher (name, pwd, sex,subject,wages,score) VALUES ( '%s', '%s', '%s','%s','3000','0')",
                    Tech->name, Tech->pwd, Tech->sex, Tech->subject);
            ret = execSql(pDB, zcSql);
            error("插入数据表失败", ret);
            sprintf(zcSql2, "INSERT INTO UserState (name,Teacher,Student,[Group],Private) VALUES ( '%s','T','0','0','0')",
                    Tech->name);
            ret = execSql(pDB, zcSql2);
            error("插入数据表失败", ret);
        }
        ret = send(sID, &msg, sizeof(msg), 0);
        error("发送数据出错", ret);
    }
    else if (msg.type == TeacherLogin) // 老师的登录
    {
        // 把缓冲区的类型转为注册，因为注册和登录用的是同一个结构体
        TEN *Tech = (TEN *)msg.buf;
        ret = 0;
        // 因为登录成功了，所以把消息类型改为ok，再发回去
        msg.type = no;
        char sql[512];
        sprintf(sql, "select * from teacher where name = '%s' AND pwd = '%s';", Tech->name, Tech->pwd);
        QUREY_RST qr;
        // 执行查询语句
        querySql(pDB, sql, &qr);
        // 如果结果的行数是>0的，说明数据库中有记录，那么登陆成功
        if (qr.row > 0)
        {
            // 因为登录成功了，所以把消息类型改为ok，再发回去
            msg.type = ok;
            // 把登录成功的用户名和地址保存起来
            strcpy(loginInfoArr[loginCount].name, Tech->name);
            loginInfoArr[loginCount].fd = sID; // 登录成功后记住用户的套接字
            loginCount++;                      // 登录的人数加1
            // 把包含有一条聊天记录的消息发送给客户端
            int ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
        else
        {
            // 把消息发回客户端，告诉它没有成功了
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
    }
    else if (msg.type == findteacher) // 打印所有老师名字
    {
        char cxsql[512];
        sprintf(cxsql, "select * from UserState where Teacher = 'T';");
        QUREY_RST qr;
        TESC in;
        querySql(pDB, cxsql, &qr);
        int jump = qr.col;
        for (size_t i = 0; i < qr.row; i++)
        {
            strcpy(in.teachername, qr.pzResult[jump + i * jump]); // 留言的作者
            memcpy(msg.buf, &in, sizeof(PVCH));
            // 把包含有一条聊天记录的消息发送给客户端
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
        msg.type = viewover; // 查询结束
        //  把查询的消息发送给客户端
        ret = send(sID, &msg, sizeof(msg), 0);
        error("发送数据出错", ret);
    }
    else if (msg.type == changeteacherscore) // 修改老师评分
    {
        TESC *pSL = (TESC *)msg.buf;
        char cxsql[512];
        // 去老师表中找到老师的工资和评分
        sprintf(cxsql, "select * from teacher where name = '%s';", pSL->teachername);
        QUREY_RST qr;
        querySql(pDB, cxsql, &qr);
        int i = atoi(qr.pzResult[10]); // 工资
        int j = atoi(qr.pzResult[11]); // 评分
        if (pSL->score <= 2)
        {
            char sql[512];
            j = j + pSL->score;
            i = i - 300;
            sprintf(sql, "UPDATE teacher SET score = '%d'WHERE name = '%s';", j, pSL->teachername);
            ret = execSql(pDB, sql);
            error("插入数据表失败", ret);
            char sql1[512];
            sprintf(sql1, "UPDATE teacher SET wages = '%d'WHERE name = '%s';", i, pSL->teachername);
            ret = execSql(pDB, sql1);
            error("插入数据表失败", ret);
        }
        else if (3 <= pSL->score && pSL->score <= 4)
        {
            char sql[512];
            j = j + pSL->score;
            i = i + 300;
            sprintf(sql, "UPDATE teacher SET score = '%d'WHERE name = '%s';", j, pSL->teachername);
            ret = execSql(pDB, sql);
            error("插入数据表失败", ret);
            char sql1[512];
            sprintf(sql1, "UPDATE teacher SET wages = '%d'WHERE name = '%s';", i, pSL->teachername);
            ret = execSql(pDB, sql1);
            error("插入数据表失败", ret);
        }
        else if (pSL->score == 5)
        {
            char sql[512];
            j = j + pSL->score;
            i = i + 888;
            sprintf(sql, "UPDATE teacher SET score = '%d'WHERE name = '%s';", j, pSL->teachername);
            ret = execSql(pDB, sql);
            error("插入数据表失败", ret);
            char sql1[512];
            sprintf(sql1, "UPDATE teacher SET wages = '%d'WHERE name = '%s';", i, pSL->teachername);
            ret = execSql(pDB, sql1);
            error("插入数据表失败", ret);
        }
    }
    else if (msg.type == inprivate) // 用户点进私聊
    {
        char sql[512];
        sprintf(sql, "UPDATE UserState SET Private = 'P'WHERE name = '%s';", msg.buf);
        ret = execSql(pDB, sql);
        error("插入数据表失败", ret);
    }
    else if (msg.type == outprivate) // 用户退出私聊
    {
        PVCH *pSL = (PVCH *)msg.buf;
        char sql[512];
        sprintf(sql, "UPDATE UserState SET Private = '0'WHERE name = '%s';", pSL->selfName);
        ret = execSql(pDB, sql);
        error("插入数据表失败", ret);
    }
    else if (msg.type == Inquire) // 私聊用户遍历
    {
        PVCH *pSL = (PVCH *)msg.buf;
        char cxsql[512];
        sprintf(cxsql, "select * from UserState where name = '%s';", pSL->destName); // 去所有用户里面找私聊的对象
        QUREY_RST qr;
        querySql(pDB, cxsql, &qr);
        if (qr.row > 0)
        {
            if (strcmp(qr.pzResult[9], "P") == 0)
            {
                msg.type = ok;
                ret = send(sID, &msg, sizeof(msg), 0);
                error("发送数据出错", ret);
                return 0;
            }
            else
            {
                msg.type = no;
                ret = send(sID, &msg, sizeof(msg), 0);
                error("发送数据出错", ret);
                return 0;
            }
        }
        else
        {
            msg.type = notfind;
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
    }
    else if (msg.type == privatechat) // 私聊
    {
        PVCH *pSL = (PVCH *)msg.buf;
        printf("收到了%s发来的消息，目标是：%s . 内容是:%s\n", pSL->selfName, pSL->destName, pSL->text);
        //  找到是私聊状态的人
        char cxsql[512];
        sprintf(cxsql, "select * from UserState where name = '%s'and Private = 'P';", pSL->destName); // 去所有用户里面找私聊的对象
        QUREY_RST qr;
        querySql(pDB, cxsql, &qr);
        int ret = 0;
        int j = 0;
        for (size_t i = 0; i < loginCount; i++)
        {
            if (strcmp(loginInfoArr[i].name, pSL->destName) == 0) // 在登录数组找到私聊对象的套接字
            {
                j = i;
                break;
            }
        }
        if (qr.row > 0)
        {
            msg.type = ok;
            char timeStr[64];
            time_t now = time(0);
            strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", localtime(&now));
            strcpy(pSL->t, timeStr);
            // 把消息发送给客户端
            ret = send(loginInfoArr[j].fd, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
            saveChatHistory(pSL);
        }
    }
    else if (msg.type == message) // 留言板
    {
        PVCH *Ply = (PVCH *)msg.buf;
        char zcSql[512];
        char timeStr[64];
        time_t now = time(0);
        strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", localtime(&now));
        sprintf(zcSql, "INSERT INTO message (selfname, destname,text,time) VALUES ( '%s', '%s', '%s','%s')",
                Ply->selfName, Ply->destName, Ply->text, timeStr);
        int ret = execSql(pDB, zcSql);
        error("插入数据表失败", ret);
        saveChatHistory(Ply);
    }
    else if (msg.type == viewmessage) // 查看留言板
    {
        char cxsql[512];
        sprintf(cxsql, "select * from message where destname = '%s';", msg.buf); // 去留言里面找自己的名字
        QUREY_RST qr;
        querySql(pDB, cxsql, &qr);
        PVCH in;
        int jump = qr.col;
        if (qr.row > 0)
        {
            for (size_t i = 0; i < qr.row; i++)
            {
                strcpy(in.selfName, qr.pzResult[jump + i * 4]); // 留言的作者
                strcpy(in.text, qr.pzResult[jump + i * 4 + 2]); // 留言的内容
                strcpy(in.t, qr.pzResult[jump + i * 4 + 3]);    // 留言的时间
                memcpy(msg.buf, &in, sizeof(PVCH));
                // 把包含有一条聊天记录的消息发送给客户端
                ret = send(sID, &msg, sizeof(msg), 0);
                error("发送数据出错", ret);
            }
            msg.type = viewover; // 查询结束
            //  把查询的消息发送给客户端
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
        else
        {
            msg.type = no;
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
    }
    else if (msg.type == howmessage) // 查看留言行数
    {
        char cxsql[512];
        sprintf(cxsql, "select * from message where destname = '%s';", msg.buf); // 去所有用户里面找自己的名字
        QUREY_RST qr;
        querySql(pDB, cxsql, &qr);
        char str[10];
        sprintf(str, "%d", qr.row); // 整型转字符串
        memcpy(msg.buf, &str, sizeof(qr));
        int ret = send(sID, &msg, sizeof(msg), 0);
        error("发送数据出错", ret);
    }
    else if (msg.type == delemessage) // 删除留言
    {
        char sql1[512];
        sprintf(sql1, "DELETE from  message where destname='%s';", msg.buf);
        execSql(pDB, sql1); // 执行数据库语言的操作
    }
    else if (msg.type == groupchat) // 群聊
    {
        PVCH *pSL = (PVCH *)msg.buf;
        printf("收到了%s发来的消息,内容是:%s\n", pSL->selfName, pSL->text);
        // 遍历所有登录的人
        for (size_t i = 0; i < loginCount; i++) // 此时loginCount作为下标最大值
        {
            if (strcmp(loginInfoArr[i].name, pSL->selfName) == 0)
            {
                continue;
            }
            else
            {
                msg.type = ok;
                // pSL->t = time(0); // 记录发消息的时间
                char timeStr[64];
                time_t now = time(0);
                strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", localtime(&now));
                strcpy(pSL->t, timeStr);
                // 把消息发送给客户端
                int ret = send(loginInfoArr[i].fd, &msg, sizeof(msg), 0);
                if (ret == -1)
                {
                    printf("发送数据出错了\n");
                    break;
                }
                saveChatHistory(pSL);
            }
        }
    }
    else if (msg.type == viewhomework) // 查看作业
    {
        char cxsql[512];
        sprintf(cxsql, "select * from  HomeWork where subject = '%s';", msg.buf); // 查看学生输入的科目
        QUREY_RST qr;
        HWORK in;
        querySql(pDB, cxsql, &qr);
        int jump = qr.col;
        if (qr.row > 0)
        {
            for (size_t i = 0; i < qr.row; i++)
            {
                strcpy(in.teacher, qr.pzResult[jump + i * jump]);      // 教师名字
                strcpy(in.subjects, qr.pzResult[jump + i * jump + 1]); // 科目
                strcpy(in.text, qr.pzResult[jump + i * jump + 2]);     // 作业
                memcpy(msg.buf, &in, sizeof(HWORK));
                // 把包含有一条聊天记录的消息发送给客户端
                int ret = send(sID, &msg, sizeof(msg), 0);
                error("发送数据出错", ret);
            }
            msg.type = viewover; // 查询结束
            //  把查询的消息发送给客户端
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
        else
        {
            msg.type = no;
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
    }
    else if (msg.type == dohomework) // 写作业
    {
        HWORK *pSL = (HWORK *)msg.buf;
        char zcSql[512];
        sprintf(zcSql, "INSERT INTO DoHomework (Teacher, Student,subject,text) VALUES ( '%s', '%s', '%s','%s')",
                pSL->teacher, pSL->student, pSL->subjects, pSL->text);
        ret = execSql(pDB, zcSql);
        error("插入数据表失败", ret);
    }
    else if (msg.type == homework) // 布置作业
    {
        HWORK *pSL = (HWORK *)msg.buf;
        char cxsql[512];
        sprintf(cxsql, "select * from teacher where name = '%s';", pSL->teacher); // 找老师教的科目
        QUREY_RST qr;
        querySql(pDB, cxsql, &qr);
        int jump = qr.col;
        strcpy(pSL->subjects, qr.pzResult[9]);

        char zcSql[512];
        // 把用户名和密码插入到数据表中
        sprintf(zcSql, "INSERT INTO Homework (name,subject,text) VALUES ( '%s', '%s', '%s')",
                pSL->teacher, pSL->subjects, pSL->text);
        ret = execSql(pDB, zcSql);
        error("插入数据表失败", ret);
    }
    else if (msg.type == viewdohomework) // 查看答案
    {
        char cxsql[512];
        sprintf(cxsql, "select * from  DoHomeWork where Teacher = '%s';", msg.buf); // 查看教师布置作业的学生答案
        printf("%s\n", msg.buf);
        QUREY_RST qr;
        HWORK in;
        querySql(pDB, cxsql, &qr);
        int jump = qr.col;
        if (qr.row > 0)
        {
            for (size_t i = 0; i < qr.row; i++)
            {
                strcpy(in.student, qr.pzResult[jump + i * jump + 1]);  // 学生名字
                strcpy(in.subjects, qr.pzResult[jump + i * jump + 2]); // 科目
                strcpy(in.text, qr.pzResult[jump + i * jump + 3]);     // 作业
                memcpy(msg.buf, &in, sizeof(HWORK));
                ret = send(sID, &msg, sizeof(msg), 0);
                error("发送数据出错", ret);
            }
            msg.type = viewover; // 查询结束
            //  把查询的消息发送给客户端
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
        else
        {

            msg.type = no;
            ret = send(sID, &msg, sizeof(msg), 0);
            error("发送数据出错", ret);
        }
    }
    else if (msg.type == changescore) // 修改学生成绩
    {
        HWORK *pSL = (HWORK *)msg.buf;
        char cxsql[512];
        // 去学生表中找到学生的成绩
        sprintf(cxsql, "select * from student where name = '%s';", pSL->student);
        QUREY_RST qr;
        querySql(pDB, cxsql, &qr);
        int i = atoi(qr.pzResult[9]); // 将学生的成绩赋值给i
        if (strcmp(pSL->yorno, "y") == 0)
        {
            char sql[512];
            i = i + 1;
            sprintf(sql, "UPDATE student SET score = '%d'WHERE name = '%s';", i, pSL->student);
            ret = execSql(pDB, sql);
            error("插入数据表失败", ret);
        }
        else if (strcmp(pSL->yorno, "n") == 0)
        {
            return 0;
        }
    }
    else if (msg.type == viewteacher) // 查看教师信息
    {
        char cxsql[512];
        sprintf(cxsql, "select * from teacher where name = '%s';", msg.buf); // 去所有用户里面找自己的名字
        QUREY_RST qr;
        TEN in;
        querySql(pDB, cxsql, &qr);
        int jump = qr.col;
        strcpy(in.name, qr.pzResult[jump]);        // 教师名字
        strcpy(in.pwd, qr.pzResult[jump + 1]);     // 密码
        strcpy(in.sex, qr.pzResult[jump + 2]);     // 性别
        strcpy(in.subject, qr.pzResult[jump + 3]); // 科目
        in.wages = atoi(qr.pzResult[jump + 4]);
        in.score = atoi(qr.pzResult[jump + 5]);
        memcpy(msg.buf, &in, sizeof(TEN));
        // 把包含有一条聊天记录的消息发送给客户端
        ret = send(sID, &msg, sizeof(msg), 0);
        error("发送数据出错", ret);
    }
    else if (msg.type == viewstudent) // 查看学生信息
    {
        char cxsql[512];
        sprintf(cxsql, "select * from student where name = '%s';", msg.buf); // 去所有用户里面找自己的名字
        QUREY_RST qr;
        SEN in;
        querySql(pDB, cxsql, &qr);
        int jump = qr.col;
        in.id = atoi(qr.pzResult[jump]);
        strcpy(in.name, qr.pzResult[jump + 1]); // 学生名字
        strcpy(in.pwd, qr.pzResult[jump + 2]);  // 密码
        strcpy(in.sex, qr.pzResult[jump + 3]);  // 性别
        in.score = atoi(qr.pzResult[jump + 4]); // 成绩
        memcpy(msg.buf, &in, sizeof(SEN));
        // 把包含有一条聊天记录的消息发送给客户端
        int ret = send(sID, &msg, sizeof(msg), 0);
        error("发送数据出错", ret);
    }
}
int main(int c, char *arg[])
{
    sqlite3 *db = NULL; // 定义一个sqlite3的数据库指针
    // 打开指定的数据库文件，如果不存在将创建一个同名的数据库文件
    // 打开或者创建一个数据库，名字叫test.db，并且把数据用指针db保存
    int data = sqlite3_open("school.db", &db);
    error("数据库出现异常", data);
    pDB = db;
    // 声明结构体接受数据
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin)); // 清零
    sin.sin_family = AF_INET;     // IPV4的网络地址版本
    sin.sin_port = htons(9000);   // 把9000号端口转为网络字节序并赋值给端口参数

    u_int32_t ip = 0;
    // 把字符串ip地址转为网络字节序ip
    inet_pton(AF_INET, "192.168.91.60", &ip);
    sin.sin_addr.s_addr = ip;

    // 创建一个tcp套接字，参数1指定地址版本，参数2指定协议，SOCK_STREAM代表是TCP，SOCK_DGRAM代表UDP
    int sID = socket(AF_INET, SOCK_STREAM, 0);
    error("创建套接字失败", sID);
    // 使端口号马上能被重新使用
    int opt = 1;
    int set = setsockopt(sID, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    error("设置端口号重用失败", set);
    // 把套接字和地址绑定（手机+手机号）
    if (-1 == bind(sID, (struct sockaddr *)&sin, sizeof(sin)))
    {
        perror("绑定地址失败,请等待30秒或者换一个端口试试");
        return 0;
    }

    // 打开监听功能，监听绑定地址上的来访请求，5代表同时允许5个客户端排队连接
    listen(sID, 5);

    // 定义MAX_EVENT个事件结构体，每个结构体中都有感兴趣的事件和套接字
    struct epoll_event ev, events[MAX_EVENT];
    int epoll_id = epoll_create(MAX_EVENT);
    ev.events = EPOLLIN;
    ev.data.fd = sID;
    epoll_ctl(epoll_id, EPOLL_CTL_ADD, sID, &ev);

    while (1)
    {
        int count = epoll_wait(epoll_id, events, MAX_EVENT, -1);

        if (count == -1)
        {
            perror("epoll_wait");
            return 0;
        }

        for (size_t i = 0; i < count; i++)
        {
            int fd = events[i].data.fd;
            printf("fd = %d\n", fd);
            if (fd == sID) // 说明i是监听套接字
            {
                printf("因为i(%lu)就是监听套接字，它上面发生的可读事件是说明有用户来连接我们了\n", i);
                struct sockaddr_in clientAddr;
                socklen_t len = sizeof(clientAddr);
                int newFd = accept(fd, (struct sockaddr *)&clientAddr, &len);
                printf("产生了一个新的套接字，快为它准一个结构体，并加入到epoll对象中管理起来吧\n");
                ev.data.fd = newFd; // 把新的套接字赋值给结构体
                // epoll操作和控制，EPOLL_CTL_ADD是往epoll对象中添加结构体
                if (0 == epoll_ctl(epoll_id, EPOLL_CTL_ADD, newFd, &ev))
                {
                    printf("套接字加入epoll成功\n");
                }
            }
            else
            {
                MSG msg;
                int ret = read(fd, &msg, sizeof(MSG));
                if (ret <= 0)
                {
                    // 把要移除的套接字结构体准备好
                    ev.data.fd = fd;
                    // 把套接字结构体从epoll中移除掉
                    if (0 == epoll_ctl(epoll_id, EPOLL_CTL_DEL, fd, &ev))
                    {
                        printf("从epoll中移除套接字结构体成功\n");
                    }
                    else
                    {
                        perror("epoll");
                        printf("从epoll中移除套接字结构体失败\n");
                    }
                    close(fd);
                    continue; // 指向下一个循环
                }
                else
                {
                    // printf("收到客户端的消息：%s\n", buf);
                    work(fd, msg);
                }
            }
        }
    }
    close(sID);
    return 0;
}